/*
 * Queue.h
 *
 *  Created on: Jun 13, 2014
 *      Author: brightchen
 */

#ifndef MYQUEUE_H_
#define MYQUEUE_H_

#include "SimpleLocker.h"

template< class T, int S=100 > class MyQueue
{
private:
  T data[S];
  T* pHead;
  T* pTail;
  pthread_mutex_t locker;
//  pthread_mutex_t writeLocker;
  pthread_mutex_t queueFullLocker;
  pthread_mutex_t queueEmptyLocker;

public:
  MyQueue ()
  {
    pHead = NULL;
    pTail = NULL;
  }

  virtual ~MyQueue (){};

  virtual void add( const T& value )
  {
    while(true)
    {
      pthread_mutex_lock( &locker );
      if( !isQueueFull() )
      {
        break;
      }

      pthread_mutex_unlock( &locker );
      holdForReading();
    }
    bool isEmpty = ( pHead == NULL );

    increaseTail();
    *pTail = value;

    if( isEmpty )
    {
      //was empty, notify can get data now
      pthread_mutex_unlock( &queueEmptyLocker );
    }
    if( isQueueFull() )
    {
      //currently full, can't add any more data
      pthread_mutex_lock( &queueFullLocker );
    }
    pthread_mutex_unlock( &locker );

  }

  virtual void get( T& value )
  {
    while( true )
    {
      pthread_mutex_lock( &locker );
      if( pHead != NULL )
        break;

      pthread_mutex_unlock( &locker );
      holdForWriting();
    }

    bool isFull = isQueueFull();
    value = *pHead;
    increaseHead();
    if( isFull )
    {
      // notify not full any more
      pthread_mutex_unlock( &queueFullLocker );
    }
    if( pHead == NULL )   //empty
      pthread_mutex_lock( &queueEmptyLocker );

    pthread_mutex_unlock( &locker );
  }

protected:

  /**
   * wait for a reading as current queue is full
   */
  void holdForReading()
  {
    SimpleLocker full( queueFullLocker );
  }

  /**
   * wait for a writing as current queue is empty
   */
  void holdForWriting()
  {
    SimpleLocker empty( queueEmptyLocker );
  }

  bool isQueueFull()
  {
    if( pTail == NULL )
      return false;
    return ( pTail >= pHead ) ? ( pTail - pHead + 1 == S ) : ( pHead == pTail + 1 );
  };

  /**
   * called before adding value into the queue to move tail to the first empty position
   */
  inline void increaseTail()
  {
    if( pTail == NULL )
    {
      pTail = data;
      assert( pHead == NULL );
      pHead = data;
      return;
    }

    //the isFull() function should be called to make the queue is not full before calling this function
    if( ++pTail >= data + S )
      pTail = data;
  }

  /**
   * called after retrieved a value and move to next value for next retrieving.
   */
  inline void increaseHead()
  {
    assert( pHead != NULL );
    if( pHead == pTail )
    {
      //there was only one data left.
      pHead = pTail = NULL;
      return;
    }

    if( ++pHead >= data + S )
      pHead = data;

  }

};


#endif /* MYQUEUE_H_ */
